﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;

namespace KATS_Client.UI
{
    /// <summary>
    /// The window manager 
    /// </summary>
    static class WindowManager
    {

        /// <summary>
        /// Windows or tabs have changed
        /// </summary>
        public static event EventHandler<EventArgs> DisplayChanged;

        /// <summary>
        /// The window set has been changed. [window added or removed]
        /// </summary>
        public static event EventHandler<EventArgs> WindowSetChanged;

        /// <summary>
        /// Static constructor
        /// </summary>
        static WindowManager()
        {
            int rv = ProgressReporting.Start("Loading window data");
            Load();
            ProgressReporting.End(rv, "Loaded window data");
            Client.FileActionsQueue.FileActionCompleted += new EventHandler<KATS_Client.Client.FileActionProgress>(FileActionsQueue_FileActionCompleted);
            DisplayChanged += new EventHandler<EventArgs>(WindowManager_DisplayChanged);

            Program.Exiting += new EventHandler<EventArgs>(Program_Exiting);
        }

        /// <summary>
        /// The program is exiting
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        static void Program_Exiting(object sender, EventArgs e)
        {
            // save the window data if needs to be saved
            if (SaveWindows_Timer != null)
            {
                // close the timer
                SaveWindows_Timer.Close();
                SaveWindows_Timer = null;
                // save the window data
                Save();
            }
        }

        /// <summary>
        /// The tabs or windows have been changed
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        static void WindowManager_DisplayChanged(object sender, EventArgs e)
        {
            // start timer to save the new tabs/windows

            if (SaveWindows_Timer == null)
            {
                // not currently waiting to save the queue
                // so create and start a save timer
                SaveWindows_Timer = new System.Timers.Timer(7000);
                SaveWindows_Timer.AutoReset = false;
                SaveWindows_Timer.Elapsed += new System.Timers.ElapsedEventHandler(SaveWindows_Timer_Elapsed);
                SaveWindows_Timer.Enabled = true;
            }
        }



        /// <summary>
        /// The save window data has been triggered
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        static void SaveWindows_Timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            Save();
            SaveWindows_Timer.Close();
            SaveWindows_Timer = null;
        }

        /// <summary>
        /// The save window timer
        /// </summary>
        static System.Timers.Timer SaveWindows_Timer;



        /// <summary>
        /// Register for window events
        /// </summary>
        /// <param name="window">The window</param>
        public static void Register(Window window)
        {
            window.WindowChanged += new EventHandler<EventArgs>(window_WindowChanged);
        }

        /// <summary>
        /// A window has changed
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        static void window_WindowChanged(object sender, EventArgs e)
        {
            if (DisplayChanged != null)
                DisplayChanged(new object(), new EventArgs());
        }

        /// <summary>Refresh all tabs, in a new thread</summary>
        public static void Refresh_AllTabs_Threaded()
        {
            System.Threading.Thread RefreshThread = new System.Threading.Thread(new System.Threading.ThreadStart(Refresh_AllTabs));
            RefreshThread.Name = "Refreshing all tab thread";
            RefreshThread.IsBackground = true;
            RefreshThread.Start();
        }

        /// <summary>Refresh all the tabs</summary>
        static void Refresh_AllTabs()
        {
            foreach (Window w in Windows)
            {
                w.Refresh_AllTabs();
            }
        }

        /// <summary>Refresh all tabs in all windows, in a new thread</summary>
        /// <param name="Remote">Refresh all remote or local tabs?</param>
        public static void Refresh_AllTabs_Threaded(bool Remote)
        {
            System.Threading.Thread RefreshThread = new System.Threading.Thread(new System.Threading.ParameterizedThreadStart(Refresh_AllTabs));
            string target = "remote";
            if (!Remote)
                target = "local";
            RefreshThread.Name = string.Format("Refreshing {0} windows thread",target);
            RefreshThread.IsBackground = true;
            RefreshThread.Start(Remote);
        }

        /// <summary>Refresh all tabs in all windows</summary>
        /// <param name="Remote">A bool value, that has been casted to an object</param>
        static void Refresh_AllTabs(object Remote)
        {
            bool remote = (bool)Remote;
            foreach (Window w in Windows)
            {
                w.Refresh_AllTabs(remote);
            }
        }

        /// <summary>Refresh only the current tabs, in a new thread</summary>
        public static void Refresh_CurrentTabs_Threaded()
        {
            System.Threading.Thread RefreshThread = new System.Threading.Thread(new System.Threading.ThreadStart(Refresh_CurrentTabs));
            RefreshThread.Name = "Refreshing current tabs thread";
            RefreshThread.IsBackground = true;
            RefreshThread.Start();
        }

        /// <summary>Refresh only the current tabs</summary>
        public static void Refresh_CurrentTabs()
        {
            foreach (Window w in Windows)
            {
                if (w.tabControl.SelectedTab != null)
                {
                    Tab t = (Tab)w.tabControl.SelectedTab.Tag;
                    t.Refresh();
                }
            }
        }

        


        /// <summary>
        /// Initialise the window manager
        /// </summary>
        public static void Initialise()
        {
            // this triggers the static constructor
        }

        /// <summary>
        /// The active windows
        /// </summary>
        public static List<Window> Windows = new List<Window>();


        /// <summary>
        /// To XML
        /// </summary>
        private static XElement ToXML()
        {
            XElement[] data = new XElement[Windows.Count];

            for (int i = 0; i < Windows.Count; i++)
            {
                data[i] = Windows[i].ToXML();
            }

            XElement element = new XElement("Windows",
                data);
            return element;
        }

        /// <summary>
        /// Save the windows
        /// </summary>
        public static void Save()
        {
            //int rv = ProgressReporting.Start("Saving window data");

            XElement data = ToXML();

            XDocument document =
                 new XDocument(
                     new XDeclaration("1.0", "uft-8", "yes"),
                     new XComment("The window data."),
                     data);

            while (true)
            {
                try
                {
                    document.Save(Properties.Settings.Default.SystemWindows);
                    break;
                }
                catch (Exception)
                {
                    System.Threading.Thread.Sleep(100);
                    // loop until the file is saved!
                }
            }
            //ProgressReporting.End(rv, "Window data saved");
        }

        /// <summary>
        /// Load the windows
        /// </summary>
        public static void Load()
        {
            Windows.Clear();

            XDocument document = XDocument.Load(Properties.Settings.Default.SystemWindows);
            XElement elements = document.Element("Windows");
            XElement[] data = elements.Elements("Window").ToArray();

            foreach (XElement x in data)
            {
                Windows.Add(new Window(x));
            }

            while (Windows.Count < 2)
            {
                // if there are less than 2 windows in the system
                // add a new blank window to fix this
                Add_EmptyWindow(true);
            }
        }

        /// <summary>Refresh a tab pointed at the directory</summary>
        /// <param name="directory">The directory</param>
        private static void RefreshDirectory(Client.FileSystemObject directory)
        {
            string directoryString = directory.ToString();
            foreach (Window w in Windows)
            {
                foreach (Tab t in w.Tabs)
                {
                    if (t.Directory.ToString() == directoryString)
                    {
                        t.Refresh_Threaded();
                    }
                }
            }
        }

        /// <summary>
        /// When an item in the queue is completed, this triggers
        /// </summary>
        /// <param name="sender">The FileActionData that has completed</param>
        /// <param name="e"></param>
        static void FileActionsQueue_FileActionCompleted(object sender, KATS_Client.Client.FileActionProgress e)
        {
            if (sender is Client.FileActionData)
            {
                // then this is a file action data, that has completed
                Client.FileActionData fad = (Client.FileActionData)sender;

                Client.FileSystemObject[] locations = new Client.FileSystemObject[2];
                locations[0] = fad.Location;
                locations[1] = fad.NewLocation;

                foreach (Client.FileSystemObject f in locations)
                {
                    if (f != null)
                    {
                        Client.FileSystemObject parent = f.Parent;
                        RefreshDirectory(parent);
                    }
                }
            }
        }

        /// <summary>
        /// Add a blank window to the screen
        /// </summary>
        /// <param name="IsLocal">Is the window to be local?</param>
        public static void Add_EmptyWindow(bool IsLocal)
        {
            Client.FileSystemObject emptyTabLocation = new Client.FileSystemObject("", !IsLocal, Client.FileSystemObjectTypes.Directory);

            XElement EmptyWindow =
                new XElement("Window",
                    emptyTabLocation.ToXML(),
                    new XElement("ActiveTab", 0)
                    );

            Window w = new Window(EmptyWindow);
            Windows.Add(w);

            // triger a safe window event
            if (DisplayChanged != null)
                DisplayChanged(new object(), new EventArgs());

            // update the displayed windows
            if (WindowSetChanged != null)
                WindowSetChanged(new object(), new EventArgs());
        }

        /// <summary>
        /// Remove a window from the system
        /// </summary>
        /// <param name="window">The window to remove</param>
        public static void Remove_Window(Window window)
        {
            // remove the window from the set
            Windows.Remove(window);

            while (Windows.Count < 2)
            {
                // if there are less than 2 windows in the system
                // add a new blank window to fix this
                Add_EmptyWindow(true);
            }

            // save the window changed event
            if (DisplayChanged != null)
                DisplayChanged(new object(), new EventArgs());

            // update the displayed windows
            if (WindowSetChanged != null)
                WindowSetChanged(new object(), new EventArgs());
        }
    }
}
